﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using ArtMath.Component.Line.Converter;
using ArtMath.Core.Data;
using ArtMath.PointCollection;
using ArtMath.Component.Point;
using ArtMath.Attribute;
using ArtMath.Helper;
using ArtMath.Flag;
using ArtMath.Core.Helper;
using ArtMath.Resource;
using ArtMath.CommonType;

namespace ArtMath.Component.Line
{
    /// <summary>
    /// 线段基类
    /// </summary>
    [Serializable, TypeConverter(typeof(LineSegmentBaseConverter))]
    public abstract class LineSegmentBase : StraightLineBase
    {
        PropertyDisplayer lenDisplayer;
        List<KeyValuePair<PointD, PointD>> track = new List<KeyValuePair<PointD, PointD>>();//不能用Dictionary，因为键值可能重复
        public LineSegmentBase(SketchPad pad, string name, ToolTypeName toolType, bool addToList = true) : base(pad, name, toolType, addToList)
        {
            InPoints = new PointBase[2];
            LengthDisplayer = new PropertyDisplayer(this, "Length");
        }
        public override PointCollectionsManager Skeleton
        {
            get
            {
                return new PointCollectionsManager(new LineSegment(Point1.ToPointD(), Point2.ToPointD()));
            }
        }
        public override PointCollectionsManager PSkeleton
        {
            get
            {
                return new PointCollectionsManager(new LineSegment(Point1.Pxy, Point2.Pxy));
            }
        }
        public override PointCollectionsManager NewSkeleton
        {
            get
            {
                return new PointCollectionsManager(new LineSegment(Point1.NewXY, Point2.NewXY));
            }
        }
        public override PointCollectionsManager NewPSkeleton
        {
            get
            {
                return new PointCollectionsManager(new LineSegment(Point1.NewPxy, Point2.NewPxy));
            }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "LSLength", "Displayer")]
        public PropertyDisplayer LengthDisplayer
        {
            get { return lenDisplayer; }
            set { lenDisplayer = value; }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("{0}1", "EndPoint")]
        public virtual PointBase Point1
        {
            get { return InPoints[0]; }
            set { SetPoint(0, value); }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("{0}2", "EndPoint")]
        public virtual PointBase Point2
        {
            get { return InPoints[1]; }
            set { SetPoint(1, value); }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("LSLength")]
        public virtual double Length { get; protected set; }
        [Browsable(false)]
        public virtual double NewLength
        {
            get { return Point1.NewXY.DistanceTo(Point2.NewXY); }
        }
        [Browsable(false)]
        public PointD MidPoint
        {
            get { return (Point1.ToPointD() + Point2.ToPointD()) / 2; }
        }
        [Browsable(false)]
        public PointD NewMidPoint
        {
            get { return (Point1.NewXY + Point2.NewXY) / 2; }
        }
        [Browsable(false)]
        public PointD PMidPoint
        {
            get { return (Point1.Pxy + Point2.Pxy) / 2; }
        }
        public override double NewSlope
        {
            get
            {
                return Point1.NewXY.GetSlope(Point2.NewXY);
            }
        }
        public override void RefreshPropertyDisplayersLocation()
        {
            LengthDisplayer.RefreshLocation();
            base.RefreshPropertyDisplayersLocation();
        }
        public override void RefreshPropertyDisplayersText()
        {
            LengthDisplayer.RefreshText();
            base.RefreshPropertyDisplayersText();
        }
        public override double NewPSlope
        {
            get
            {
                return Point1.NewPxy.GetSlope(Point2.NewPxy);
            }
        }
        public override void PaintTrack(Graphics gra)
        {
            if (ShowTrack_)
            {
                for (int i = 0; i < track.Count; i++)
                {
                    gra.DrawLineSegment(TrackPen, track[i].Key, track[i].Value);
                }
            }
            base.PaintTrack(gra);
        }
        public override void CheckInRegion(RectangleD rect)
        {
            SetInPicker(rect.IntersectLineSegment(Point1.Pxy, Point2.Pxy));
            base.CheckInRegion(rect);
        }
        public void CreateComponents(PointBase point, bool p2DoBuilding = false)
        {
            if (point == null) return;
            if (Point1 == null)
            {
                Point1 = point;
                SpareCoordinate_ = Point1.Pxy;
                Pad.TrackObj = this;
            }
            else
            {
                if (p2DoBuilding)
                {
                    Pad.HostTrackComponentBuilding(point.Pxy);
                }
                Point2 = point;
                base.CreateComponents();
            }
        }
        public void CreateComponents(string pointName, PointCollectionBase[] collections = null)
        {
            if (collections == null)
            {
                if (Point1 == null)
                {
                    collections = Universal.GenerateInstanceArray();
                }
                else
                {
                    collections = new PointCollectionBase[1];
                    if (Flags.Shift && AllowFix)
                    {
                        collections[0] = new Ray(Point1.ToPointD(), Point1.ToPointD().GetAngle(Pad.PxyToXY(SpareCoordinate)) );
                    }
                    else
                    {
                        collections[0] = new Universal();
                    }
                    collections[0].Exceptions = new HashSet<PointCollectionBase>() { new DiscontinuityPoint(Point1.ToPointD()) };
                }
            }
            CreateComponents(Pad.CreatePoint(pointName, SpareCoordinate_, true, collections));
        }
        public void CreateComponents(PointBase pt1, PointBase pt2)
        {
            if (pt1 == null || pt2 == null) return;
            CreateComponents(pt1);
            CreateComponents(pt2, true);
        }
        public override PointD GetOutFreePointRecommendNewLocation(PointBase pt, bool getXY = true)
        {
            return getXY ? Utility.RatePoint(Point1.NewXY, Point2.NewXY, pt.ToPointD().GetRate(Point1.ToPointD(), Point2.ToPointD())) :
                Utility.RatePoint(Point1.NewPxy, Point2.NewPxy, pt.Pxy.GetRate(Point1.Pxy, Point2.Pxy));
        }
        public override string ToString()
        {
            return $"{Point1}, {Point2}";
        }
        public override PropertyDisplayer[] PropertyDisplayersCheckNearBy(PointD Plocation)
        {
            List<PropertyDisplayer> res = new List<PropertyDisplayer>(base.PropertyDisplayersCheckNearBy(Plocation));
            if (LengthDisplayer.InRegion(Plocation))
            {
                res.Add(LengthDisplayer);
            }
            return res.ToArray();
        }
        public override void ComponentBuilding(Base component)
        {
            if (Point1 == null) return;
            if (Flags.Shift && AllowFix)
            {
                SpareCoordinate = SpareCoordinate.GetProjectivePoint(Point1.Pxy, Math.Tan(Utility.GetFixedAngle(
                        Point1.Pxy.GetAngle(SpareCoordinate))));
            }
            PointD xy = Pad.PxyToXY(SpareCoordinate);
            Length = Point1.ToPointD().DistanceTo(xy);
            Slope = Point1.ToPointD().GetSlope(xy);
            PSlope = Point1.Pxy.GetSlope(SpareCoordinate);
            OtherBuildingTransactions(xy);
            base.ComponentBuilding(component);
        }
        protected virtual void OtherBuildingTransactions(PointD xy)
        {

        }
        public override void ComponentChanged(ShapeBase component)
        {
            Length = NewLength;
            PSlope = NewPSlope;
            base.ComponentChanged(component);
        }
        public override void SetFromString(string exp)
        {
            string[] tokens = exp.Split(',', '，');
            if (tokens.Length == 4)
            {
                Point2.TempMoveLock = true;
                try
                {
                    Point1.SetFromString(string.Join(",", tokens, 0, 2));
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    Point2.TempMoveLock = false;
                }
                Point1.TempMoveLock = true;
                try
                {
                    Point2.SetFromString(string.Join(",", tokens, 2, 2));
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    Point1.TempMoveLock = false;
                }
            }
            else
            {
                throw new ArgumentException(string.Format(Resources.Translate("AcceptNArgs"), Resources.Translate("LineSegment"), 4) + Resources.ColonSeparate + Resources.Translate("LineSegmentConverterParametersIntroduction"));
            }
        }
        public override void PaintProperties(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible)
            {
                LengthDisplayer.Paint(gra);
            }
            base.PaintProperties(gra);
        }
        public override void Paint(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible && IsCreationCompleted)
            {
                gra.DrawLineSegment(LinePen, Point1.Pxy, Point2.Pxy);
            }
            base.Paint(gra);
        }
        public override bool CanDrawVirtual
        {
            get
            {
                return Point1 != null;
            }
        }
        public override void PaintVirtualObjects(Graphics gra)
        {
            if (CanDrawVirtual)
            {
                gra.DrawLineSegment(Point1.Pxy, SpareCoordinate_);
            }
            base.PaintVirtualObjects(gra);
        }
        public override void AddTracks()
        {
            track.Add(new KeyValuePair<PointD, PointD>(Point1.Pxy, Point2.Pxy));
        }
        public override void ClearTracks()
        {
            track.Clear();
            base.ClearTracks();
        }
    }
}
